package com.vms.healthcontrol;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;

import com.vms.NetLog;
import com.vms.Settings.ValueType;
import com.vms.control.weight.input.InputSettingsFactory;
import com.vms.control.weight.model.Weight;
import com.vms.control.weight.model.WeightTime.DayPart;


public class WeightDataSource {
	
	public static final String WEIGHT_TABLE_NAME = "weight_records";
	static final String DAY = "day";
	static final String VALUE = "value";
	static final String TYPE_NAME = "type_name";
	static final String ID = "id";
	static final String WEIGHT_DB_NAME = "weightdb";
	
	NetLog netLog = new NetLog("DBProvider");
	
	protected SQLiteDatabase db;
	private Context context;


	protected synchronized void open() {
		DBOpenHelper helper = DBOpenHelper.getInstance(context);
		try {
			db = helper.getWritableDatabase();
		} catch (SQLiteException e) {
			db = helper.getReadableDatabase();
		}
	}

    protected synchronized void close() {
        DBOpenHelper helper = DBOpenHelper.getInstance(context);
        helper.close();
    }
	
    public void endTransaction() {
        try {
            db.endTransaction();
        } catch (IllegalStateException e) {
        	netLog.e( "DataSource::endTransaction already closed");
        }
    }
    
	public WeightDataSource (Context context) {
		this.context = context;
	}
	
	@Deprecated
	public void insert ( ValueUnderControl val ) {

		
		ContentValues cv = new ContentValues();
		cv.put(TYPE_NAME, val.getDbName());
		cv.put(VALUE, val.getValue());
		cv.put(DAY, val.getDbDate());
		
		long id = db.insertOrThrow(WEIGHT_TABLE_NAME, null, cv);
		netLog.v("Insert in DB. id : %d", id);
		
		db.close();
	}
	
	public boolean insertWeight (Weight weight) {
		open();
		boolean succesAdd = addWeightList(Arrays.asList(new Weight[] {weight}));
		close();
		return succesAdd;
	}
	
	public boolean insertWeightList (List<Weight> list) {
		open();
		boolean succesAdd = addWeightList(list);
		close();
		return succesAdd;
	}
	
	protected boolean addWeightList (List<Weight> list) {
		assert list != null;
		if (list.size() > 0) {
			try {
				db.beginTransaction();
				for (Weight entity : list) {
					addWithoutNewTransaction(entity);
				}
				db.setTransactionSuccessful();
				return true;
			} catch (SQLiteException e) {
				e.printStackTrace();
				return false;
			} finally {
				endTransaction();
			}
		} else {
			return true;
		}
	}
	
	protected void addWithoutNewTransaction (Weight weight) {
		ContentValues values = createContentValues(weight);
		db.insertWithOnConflict(WEIGHT_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
	}
	
	private ContentValues createContentValues(Weight weight) {
		ContentValues cv = new ContentValues();
		cv.put(TYPE_NAME, weight.getDayPart().toString());
		cv.put(VALUE, weight.getWeightValue());
		cv.put(DAY, weight.getDate().getTime());
		return cv;
	}

	public int update ( ValueUnderControl val ) {
		
		ContentValues cv = new ContentValues();
		cv.put(TYPE_NAME, val.getShowName());
		cv.put(VALUE, val.getValue());
		cv.put(DAY, val.getDbDate());
		
		int rows_updated = db.update(WEIGHT_TABLE_NAME, cv, DAY+" = ? AND "+TYPE_NAME+" = ?", new String [] {val.getDbDate(),val.getShowName()});
		
		netLog.v("Update table %s were DAY = %s AND NAME = %s.  [ rows updated: %d ]",WEIGHT_TABLE_NAME, val.getDbDate(), val.getShowName(), rows_updated);
		
		db.close();
		
		return rows_updated;
	}
	
	public int delete ( WeightSelectionBuilder where) {
//		SQLiteDatabase db = dbs.getWritableDatabase();
		return db.delete(WEIGHT_TABLE_NAME, where.getSelection(), where.getArgs());
	}
	
	public int deleteAll() {
//		SQLiteDatabase db = dbs.getWritableDatabase();
		open();
		int delete = db.delete(WEIGHT_TABLE_NAME, "1", null);
		close();
		return delete;
	}
	
	public int rowsByDate ( Date dt ) {
		
		int rows_count = 0;
		String date_long = ValueUnderControl.getDateLongStrForDb(dt);
		
		Cursor c = db.query(WEIGHT_TABLE_NAME, null, DAY+" = ?", new String[] {date_long}, null, null, null);
		
		if (c.moveToFirst()) {
	        netLog.v("In table %s where DAY=%s is %d row(s)", WEIGHT_TABLE_NAME, date_long, c.getCount());
	        rows_count = c.getCount();
	      } else
	        netLog.v("0 rows");
		
		c.close();
		db.close();
		
		return rows_count;
		 
	}
	
	public static class WeightSelectionBuilder {

		public WeightSelectionBuilder(String dateFrom, String dateTo,
				Collection<String> names) {
			super();
			this.mDateFrom = dateFrom;
			this.mDateTo = dateTo;
			this.mNames = names;
			if( dateFrom == null || dateTo == null || names == null || names.size() <= 0)
				throw new RuntimeException("Invalid params for WeightSelectionBuilder");
		}
				
		public WeightSelectionBuilder(String dateTo, Collection<String> names) {
			this(null, dateTo, names);
		}
		
		public String getSelection () {
			// ( NAME = ?{name1} OR NAME = ?{name#} ) AND ( DAY >= ?{dayFrom} AND DAY <= ?{dayTo}
			return new StringBuilder()
			.append(getNameSelection())
			.append(" AND ")
			.append(getDaySelection()).toString();
		}
		
		private String getDaySelection() {
			if(mDateFrom == null)
				return WeightDataSource.DAY + " <= ?";
			return "( " + WeightDataSource.DAY + " >= ? AND " + WeightDataSource.DAY + " <= ? )";
		}

		private String getNameSelection() {
			if(mNames.size() == 1)
				return WeightDataSource.TYPE_NAME + " = ?";
			
			StringBuilder selection = new StringBuilder();
			selection.append("( ");
			selection.append(WeightDataSource.TYPE_NAME + " = ?");
			for (int i = 1; i < mNames.size(); i++) {
				selection.append(" OR ").append(WeightDataSource.TYPE_NAME + " = ?");
			}
			selection.append(" )");
			return selection.toString();
		}

		public String[] getArgs () {
			ArrayList<String> args = new ArrayList<String>();
			args.addAll(mNames);
			
			if(mDateFrom != null)
				args.add(mDateFrom);
			
			args.add(mDateTo);
			return args.toArray(new String[args.size()]);
		}
		
		public String getOrderBy () {
			return DAY+" desc";
		}
		
		private String mDateFrom, mDateTo;
		private Collection<String> mNames;
		@Override
		public String toString() {
//			StringBuilder sb = new StringBuilder();
//			sb.append(getSelection());
//			sb.append(" | ");
//			for (String arg : getArgs()) {
//				sb.append(" ").append(arg);
//			}
//			return sb.toString();
			return String.format(getSelection().replace("?", "%s"), (Object[])getArgs());
		}
		
	}
	
	public int selectionCount ( WeightSelectionBuilder where) {
		Cursor cursor = db.query(WEIGHT_TABLE_NAME, null, where.getSelection(), where.getArgs(), null, null, where.getOrderBy());
		int count = cursor.moveToFirst() ? cursor.getCount() : 0;
		db.close();
		return count;
	}
	
	public List<ValueUnderControl> select ( WeightSelectionBuilder where ) {
		List<ValueUnderControl> values =  new ArrayList<ValueUnderControl>();
		Cursor c = db.query(WEIGHT_TABLE_NAME, null, where.getSelection(), where.getArgs(), null, null, where.getOrderBy());
		
		if (c.moveToFirst()) {

	        int idColIndex = c.getColumnIndex(ID);
	        int nameColIndex = c.getColumnIndex(TYPE_NAME);
	        int valueColIndex = c.getColumnIndex(VALUE);
	        int dayColIndex = c.getColumnIndex(DAY);

	        values = new ArrayList<ValueUnderControl>(c.getCount());
	        
	        do {
	          
	        	String nameFromDb = c.getString(nameColIndex);
    			float valueFromDb = c.getFloat(valueColIndex);
    			SimpleDateFormat format =new SimpleDateFormat("dd.MM.yyyy");
    			
    			Date dateFromDb;
				try {
					dateFromDb = new Date(Integer.parseInt(c.getString(dayColIndex)));
				} catch (Exception e) {
					e.printStackTrace();
					dateFromDb = new Date();
				}
    		
	        	
	        	netLog.v(" %d %s %f   %d = %s  [%d] ",
	        			c.getInt(idColIndex),
	        			c.getString(nameColIndex),
	        			c.getFloat(valueColIndex),
	        			c.getLong(dayColIndex));
	        	

	        	ValueUnderControl v = ValueUnderControl.create(new InputSettingsFactory(ValueType.fromString(nameFromDb)));
	        	v.setFromDatabase( nameFromDb,valueFromDb,dateFromDb );
	        	values.add(v);		

	        } while (c.moveToNext());
	        netLog.v("In table %s is %d row(s)", WEIGHT_TABLE_NAME, c.getCount());
	        
	        
	      } else
	        netLog.v("0 rows");
		
		c.close();
		db.close();
		return values;
	}
	
	
	public List<ValueUnderControl> selectByName ( String name ) {
		
		List<ValueUnderControl> values = null;
		
		
		Cursor c = db.query(WEIGHT_TABLE_NAME, null, TYPE_NAME+" = ?", new String[] {name}, null, null, DAY+" desc");
		
		if (c.moveToFirst()) {

	        int idColIndex = c.getColumnIndex(ID);
	        int nameColIndex = c.getColumnIndex(TYPE_NAME);
	        int valueColIndex = c.getColumnIndex(VALUE);
	        int dayColIndex = c.getColumnIndex(DAY);

	        values = new ArrayList<ValueUnderControl>(c.getCount());
	        
	        do {
	          
	        	String nm = c.getString(nameColIndex);
    			float val = c.getFloat(valueColIndex);
    			SimpleDateFormat format =new SimpleDateFormat("dd.MM.yyyy");
    			
    			Date dt;
				try {
					dt = new Date(Integer.parseInt(c.getString(dayColIndex)));
				} catch (Exception e) {
					e.printStackTrace();
					dt = new Date();
				}
    		
	        	
	        	netLog.v(" %d %s %f   %d = %s  [%d] ",
	        			c.getInt(idColIndex),
	        			c.getString(nameColIndex),
	        			c.getFloat(valueColIndex),
	        			c.getLong(dayColIndex));
	        	
	        	ValueUnderControl v = ValueUnderControl.create(new InputSettingsFactory(ValueType.fromString(nm)));
	        	v.setFromDatabase( nm,val,dt );
	        	values.add(v);		

	        } while (c.moveToNext());
	        netLog.v("In table %s is %d row(s)", WEIGHT_TABLE_NAME, c.getCount());
	        
	        
	      } else
	        netLog.v("0 rows");
		c.close();
		
		db.close();
		
		return values;
	}
	
	
	public int getRowsCount () {
		open();
		Cursor c = db.query(WEIGHT_TABLE_NAME, null, null, null, null, null, DAY+" desc");
		int count = c.getCount();
		c.close();
		close();
		return count;
	}
	
	public List<Weight> getAll () {
		open();
		Cursor c = db.query(WEIGHT_TABLE_NAME, null, null, null, null, null, DAY+" desc");
		List<Weight> list = readFromCursorAndClose(c);
		close();
		return list;
	}

	private List<Weight> readFromCursorAndClose(Cursor c) {
		int count = c.getCount();
		List<Weight> list = new ArrayList<Weight>(count);
		
		try {
			if(c.moveToFirst()) {
				do {
					Weight weight = getWeightFromCursor(c);
					list.add(weight);
				} while (c.moveToNext());
			}
		} finally {
			c.close();
		}
		return list;
	}
	
	private Weight getWeightFromCursor( Cursor c ) {
        int nameColIndex = c.getColumnIndex(TYPE_NAME);
        int valueColIndex = c.getColumnIndex(VALUE);
        int dayColIndex = c.getColumnIndex(DAY);
		String dayPart = c.getString(nameColIndex);
		float weightValue = c.getFloat(valueColIndex);
		Date date = new Date(c.getLong(dayColIndex));
		return new Weight(DayPart.valueOf(dayPart), weightValue, date);
	}
}
